package parteSintatica;

import java.io.IOException;
import java.util.LinkedList;

import parteLexica.AnalisadorLexico;
import parteLexica.AnalisadorLexicoConcreto;
import parteLexica.Lexema;
import parteLexica.TabelaDeTokens;
import parteLexica.TabelaDeTokens.Token;
import parteSemantica.AnalisadorSemanticoConcreto;
import parteSemantica.AvaliadorExpressao;


public class AnalisadorSintaticoConcreto implements AnalisadorSintatico{
	//Diego Sabino
		
		private LinkedList<Lexema> listaLexemas;
		private TabelaDeSimbolos tabelaDeSimbolos;		
		private AnalisadorLexico analisadorLexico;
		private int index = 0;
		private int escopo = 0;
		
		
		public AnalisadorSintaticoConcreto(){
			this.listaLexemas = new LinkedList<Lexema>();
			this.tabelaDeSimbolos = new TabelaDeSimbolos();
			this.analisadorLexico = new AnalisadorLexicoConcreto();
		}
		
		
		//a ideia que eu to na cabeça, eh percorrer toda a lista de lexemas, quando encontrar
		//algum tokem tipo, let, begin, in, if, ai verifica a estrutura dele, se tiver erro,
		//retorna uma mensagem de erro de sintaxe, senao volta e continua a percorrer a lista ate
		//encontrar um proximo lexema q precise verificar a estrutura.

		
		//temos que verificar a forma de retorno da mensagem de erro;
		public LinkedList<Lexema> analiseSintatica(String programa){
			this.listaLexemas = analisadorLexico.analiseLexica(programa);
		
			//laco que percorre toda a lista de lexemas
			for (this.index = 0; this.index < listaLexemas.size(); this.index++){
				//mudanca, aqui verificaremos a estrutura da sintaxe 
				//ainda tenho que pegar a ideia da construcao da arvore que Roberto mencionou.
				Lexema LexemaAtual = listaLexemas.get(index);
				//acho que deve ser feito um teste para verificar se o lexema atual eh
				//uma das estruturas, se for, verifica se a estrutura esta correta.
				//por exemplo:
					if(LexemaAtual.getToken() == Token.LET){
						//verificar se a estrutura do let esta correta,
						//primeiro o let, depois um identificador, depois dois pontos, depois o tipo,
						//depois o igual e por ultimo a expressao.
						//dentro do let verificado se o tipo caracteriza uma funcao ou uma variavel.
						index++;
						verificaLet();
					} else 
					
					if(LexemaAtual.getToken() == Token.BEGIN){
						verificaBegin();
					} else 
					
					if(LexemaAtual.getToken() == Token.REC){
						//verificar a estrutura
						verificaRec();
					} else
					
					if(LexemaAtual.getToken() == Token.IN){
						//verificar a estrutura
						verificaIn();
					} 
		
			}
			System.out.println(this.tabelaDeSimbolos.getTabelaDeVariaveis().get(0).getValor().top().get(0));
			return listaLexemas;
			
		}
		
		private void verificaLet(){
			//tem duas possibilidades para estrutura do let, uma para funcao e outra pra atribuicao
			//de variavel.
			
			//leva em consideracao q se for funcao deve ser obrigado o uso de parentesis 
			if(listaLexemas.get(index).getToken() == Token.IDENTIFICADOR		
			&& listaLexemas.get(index + 1).getToken() == Token.ABRE_PARENTESIS){
				this.tabelaDeSimbolos.adicionarFuncao(new Fun(listaLexemas.get(index).getNome())); 
				//vai verificar a estrutura do tipo funcao de um let
				index = index + 2;
				 verificaFuncaoDoLet(listaLexemas.get(index - 2).getNome());
			} else
			
			if ((listaLexemas.get(index).getToken() == Token.IDENTIFICADOR)		
			&& (listaLexemas.get(index + 1).getToken() == Token.DOIS_PONTOS) 
			&& (TabelaDeTokens.ehTipo(listaLexemas.get(index + 2).getToken()))
			&& (listaLexemas.get(index + 3).getToken() == Token.ATRIBUICAO)) {
				this.tabelaDeSimbolos.adicionarVariavel(new Var(listaLexemas.get(index).getNome())); 
				//verifica a estrutura do tipo comum de um let
				this.tabelaDeSimbolos.getVariavel(listaLexemas.get(index).getNome()).setTipo(listaLexemas.get(index + 2).getToken());
				this.tabelaDeSimbolos.getVariavel(listaLexemas.get(index).getNome()).setEscopo(this.escopo);
				
				index = index + 4;
				verificaAtribuicaoDoLet(listaLexemas.get(index - 4).getNome());
			}
			
		}
		
		private void verificaFuncaoDoLet(String nome){			
			while (listaLexemas.get(index).getToken() != Token.FECHA_PARENTESIS) {
				if (listaLexemas.get(index).getToken() == Token.IDENTIFICADOR &&
					listaLexemas.get(index + 1).getToken() == Token.DOIS_PONTOS &&
					TabelaDeTokens.ehTipo(listaLexemas.get(index + 2).getToken())) {
						
					tabelaDeSimbolos.getFuncao(nome).getParametrosFormais().add(
							new ParametroFormal(listaLexemas.get(index).getNome(),listaLexemas.get(index).getToken() ));
					index = index + 2;
					if (listaLexemas.get(index).getToken() == Token.VIRGULA) {
						index++;
					} 
				}
			}
			index++;
			if (listaLexemas.get(index).getToken() == Token.DOIS_PONTOS) {
				index++;
			}
			if (TabelaDeTokens.ehTipo(listaLexemas.get(index).getToken())) {
				tabelaDeSimbolos.getFuncao(nome).setTipoDeRetorno(listaLexemas.get(index).getToken());
				index++;
			}
			if (listaLexemas.get(index).getToken() == Token.ATRIBUICAO) {
				index++;
			}
			int ehEnd = 0;
			LinkedList<Lexema> expressao = new LinkedList<Lexema>();
			while ((listaLexemas.get(index).getToken() != Token.PONTO_E_VIRGULA_DUPLO) || ehEnd != 0) {
				if (listaLexemas.get(index).getToken() == Token.LET) {
					ehEnd++;
				}
				if (listaLexemas.get(index).getToken() == Token.PONTO_E_VIRGULA_DUPLO) {
					ehEnd--;
				}
				expressao.add(listaLexemas.get(index));
				index++;
			}
			tabelaDeSimbolos.getFuncao(nome).setExpressao(expressao);
			
		}
		
		
		
		private void verificaAtribuicaoDoLet(String nome){
			if (listaLexemas.get(index + 1).getToken() == Token.PONTO_E_VIRGULA_DUPLO) {
				LinkedList<String> valor = new LinkedList<String>();
				valor.add(listaLexemas.get(index).getNome());
				this.tabelaDeSimbolos.getVariavel(nome).getValor().push(valor);
				System.out.println(this.tabelaDeSimbolos.getTabelaDeVariaveis().get(0).getValor().top().get(0));
			}
			
			LinkedList<Lexema> expressao = new LinkedList<Lexema>();
			while((listaLexemas.get(index).getToken()) != Token.PONTO_E_VIRGULA_DUPLO){
				expressao.add(listaLexemas.get(index));
				index++;
			}
			AvaliadorExpressao avaliar = new AvaliadorExpressao();
			String resultado = avaliar.avaliarExpressao(expressao,tabelaDeSimbolos);
			LinkedList<String> valor = new LinkedList<String>();
			valor.add(resultado);
			tabelaDeSimbolos.getVariavel(nome).getValor().push(valor);
			
		}
		
		private void verificaBegin(){
			int indexAtual = index;
			boolean erroSintaxe = true;
			while(indexAtual < listaLexemas.size()){
				if(listaLexemas.get(indexAtual).getToken() == Token.END){
					erroSintaxe = false;
					break;
				}
			}
		}
		
		
		private void verificaRec(){
			
		}
		
		private void verificaIn(){
			
		}

		//Metodos Gets e Sets
		public LinkedList<Lexema> getLexemas() {
			return listaLexemas;
		}


		public void setLexemas(LinkedList<Lexema> lexemas) {
			listaLexemas = lexemas;
		}


		
		public void analiseSintatica(AnalisadorLexicoConcreto analisadorLexico,
				AnalisadorSemanticoConcreto analisadorSemantico) throws InterruptedException {
			AvaliadorExpressao avaliar = new AvaliadorExpressao();
			try {
				avaliar.aval();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}

}
